package im.engure.common.proxy;

import im.engure.common.exception.RPCException;
import im.engure.common.loadbalance.LoadBalanceUtil;
import im.engure.common.protocol.Invocation;
import im.engure.common.protocol.Protocol;
import im.engure.common.protocol.ProtocolFactory;
import im.engure.common.registery.LocalHostsRegistry;
import im.engure.common.registery.URL;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

public class ServiceProxyFactory {

    public static <T> T getProxy(Class<T> clazz) {

        //1.创建 clazz 类型的代理对象
        //2.当使用代理对象进行方法调用时，代理对象会代理这个方法的执行：发送请求进行远程方法调用并将结果返回给方法调用者

        Object proxyInstance = Proxy.newProxyInstance(
                ServiceProxyFactory.class.getClassLoader(),
                new Class<?>[]{clazz},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //V1
                        //直接发送请求，缺点：地址写死
                        /*
                        return HttpClient.sendHttpRequest("127.0.0.1", 8888,
                                new Invocation(clazz.getName(), method.getName(),
                                        method.getParameterTypes(), args));
                        */

                        //V2
                        /*
                        //从注册中心获取提供服务的主机列表
                        List<URL> hosts = LocalHostsRegistry.getHosts(clazz.getName());
                        //使用负载均衡策略选择可以访问的主机
                        URL targetHost = LoadBalanceUtil.randomPolicy(hosts);
                        //发送请求
                        return HttpClient.sendHttpRequest(targetHost.getHostName(),
                                targetHost.getPort(),
                                new Invocation(clazz.getName(), method.getName(),
                                        method.getParameterTypes(), args));
                         */

                        //V3
                        //简单MOCK，需要配置jvm参数 -Dmock=return:SIMPLEMOCK!
                        String mockStr = System.getProperty("mock");
                        if (mockStr != null && mockStr.startsWith("return:")) {
                            return mockStr.replace("return:", "");
                        }

                        List<URL> hosts = LocalHostsRegistry.getHosts(clazz.getName());
                        URL targetHost = LoadBalanceUtil.randomPolicy(hosts);
                        //多协议 + 配置化
                        // 注意：默认使用http协议，若要使用“dubbo协议”则需要配置jvm参数 -DprotocolName=dubbo
                        Protocol protocol = ProtocolFactory.getProtocol();
                        try {
                            return protocol.send(targetHost,
                                    new Invocation(clazz.getName(), method.getName(),
                                            method.getParameterTypes(), args));
                        } catch (RPCException e) {
                            //RPCException是我们自定义的容错异常，是我们自己抛出自己接住的，表示要返回容错信息
                            e.printStackTrace();
                            return "服务调用失败";
                        }

                    }
                });

        return (T) proxyInstance;
    }

}
